#include <cpuid.h>
#include <boost/crc.hpp>
#include "error_message.hpp"

#ifndef _crc32c_hpp
#define _crc32c_hpp

class Crc32c
{
  public:
    Crc32c();
    unsigned int value( const char *chars, unsigned int int_length );
    unsigned int value( std::string &str_in );

#ifdef DEBUG
    int test( const char *chars=NULL, unsigned int int_length=0 );
#endif // DEBUG

  private:
    boost::crc_optimal<32, 0x1EDC6F41, 0, 0, true, true>  boost_crc1;
    bool is_select_sse_u64;
    bool is_select_sse_u8;

    unsigned int intel_crc32c_u8( const char *chars, unsigned int int_length );
    unsigned int intel_crc32c_u64( const char *chars, unsigned int int_length );
    bool bool_cpu_is_support_sse4_2(void);
    unsigned int boost_crc32c_u8( const char *chars, unsigned int int_length );
};


Crc32c::Crc32c()
{
  char *chars = NULL;
  unsigned int  address_size = sizeof(chars);   //  =8判断地址长度是64位, =4判断是32位

  is_select_sse_u64 = false;
  is_select_sse_u8   = false;

  if( bool_cpu_is_support_sse4_2() )  {
    if(address_size>=8)    is_select_sse_u64 = true;  //  =8判断地址长度是64位, =4判断是32位
    else                                is_select_sse_u8    = true;
    }
  return;
}


unsigned int Crc32c::intel_crc32c_u8( const char *chars, unsigned int int_length )
{
  const char *p_begin;
  const char *p_end;
  unsigned int  int_crc = 0;

  p_begin = chars;
  p_end    = chars + int_length;

  while(  p_begin < p_end )  {
    int_crc = _mm_crc32_u8(int_crc,*p_begin);
    ++p_begin;
    continue;
    }
  return int_crc;
}


//  要求  支持64位CPU指令，　支持SSE4.2
//  运行64位系统,  测试发现即使不是8字节位对齐，也可以正常运行
unsigned int Crc32c::intel_crc32c_u64( const char *chars, unsigned int int_length )
{
  unsigned long long *long_begin;
  unsigned long long *long_end;
  unsigned int  i  ;
  unsigned int  n ;

  unsigned int long_long_size = sizeof(unsigned long long);
  //if( int_length<long_long_size)    return intel_crc32c_u8( chars, int_length );
  //  可省略, 可以处理特殊情况

  unsigned int  int_crc = 0;
  long_begin =  (unsigned long long*) chars;
  long_end    = long_begin + (int_length/long_long_size);

  while( long_begin < long_end  ) {
    int_crc = _mm_crc32_u64(int_crc, *long_begin);
    ++long_begin;
    continue;
    }

  i  = int_length/long_long_size*long_long_size;
  n = int_length;
  while(  i < n )  {
    int_crc = _mm_crc32_u8(int_crc,chars[i]);
    ++i;
    continue;
    }
  return int_crc;
}


    /*   判断处理器是否支持SSE4.2，应采取如下方法：    true if CPUID.01H:ECX.SSE4_2[bit 20] = 1
    eax == 1，则在eax中返回Family/Model/Stepping等信息,   在EBX   ECX和EDX返回一些信息
//   ecx  define bit_SSE4_2    (1 << 20)    */
bool Crc32c::bool_cpu_is_support_sse4_2(void)
{
#ifdef  __cpuid    // 在cpuid.h中定义
  unsigned int eax, ebx, ecx, edx;

  __cpuid(1, eax, ebx, ecx, edx);
  if(ecx & bit_SSE4_2)  {
     //log_message("cpu support SSE4.2",5);
     return true;
     }
#endif //  __cpuid
  if( g_display_level_default>=2 )  warn_message("__cpuid define  not find.  No use intel sse4.2 in crc32c. ");
  return false;
}


unsigned int Crc32c::boost_crc32c_u8( const char *chars, unsigned int int_length )
{
  boost_crc1.reset(0);
  boost_crc1.process_bytes(chars, int_length);
  return boost_crc1.checksum();
}


unsigned int Crc32c::value( const char *chars, unsigned int int_length )
{
  if( is_select_sse_u64 )  {
    return intel_crc32c_u64( chars, int_length );
    }
  //  32位系统，选择最简单的单字节提交,  不考虑做优化３２位的代码了，因为32位系统已经淘汰了,不值得做更多代码
  if(is_select_sse_u8)  {
    return intel_crc32c_u8( chars, int_length );
    }
  //  不支持SSE4.2的CPU, 还是软件方式吧．
  return boost_crc32c_u8( chars, int_length );
}


unsigned int Crc32c::value( std::string &str_in )
{
  if( is_select_sse_u64 )  {
    return intel_crc32c_u64( str_in.c_str(), str_in.length() );
    }
  //  32位系统，选择最简单的单字节提交,  不考虑做优化３２位的代码了，因为32位系统已经淘汰了,不值得做更多代码
  if(is_select_sse_u8)  {
    return intel_crc32c_u8( str_in.c_str(), str_in.length() );
    }
  //  不支持SSE4.2的CPU, 还是软件方式吧．
  return boost_crc32c_u8( str_in.c_str(), str_in.length() );
}


//  下面为开发过程中的测试程序,  已经验证了程序的正确性.
#ifdef DEBUG
int Crc32c::test( const char *chars_in, unsigned int int_length_in)
{
  int int_ret1;
  int int_ret2;
  int int_ret3;
  int int_ret = 0;

  char *chars ;
  unsigned int int_length=10000000;
  unsigned int i;
  bool  bool_chars_need_delete;

  if(0==int_length_in||NULL==chars_in)  {
     chars = new char[int_length];
     if(NULL==chars) {
        error_message("memory require failed.");
        return -1;
        }
     bool_chars_need_delete = true;
     for (i = 0; i <int_length; ++i)     chars[i] = i;
     }
  else {
    int_length = int_length_in;
    chars = (char *)chars_in;
    bool_chars_need_delete = false;
    }

  for(i=0;i<2;++i)  {   // 循环执行２次
  {
    boost::timer::auto_cpu_timer   a1;
    int_ret1 = boost_crc32c_u8( chars, int_length );
    log_message_int("boost_crc32c_u8",int_ret1);
  }
  {
    boost::timer::auto_cpu_timer   a2;
    int_ret2 = intel_crc32c_u8( chars, int_length );
    log_message_int("intel_crc32c_u8",int_ret2);
  }
  {
    boost::timer::auto_cpu_timer   a3;
    int_ret3 = intel_crc32c_u64( chars, int_length );
    log_message_int("intel_crc32c_u64",int_ret3);
  }
  if(int_ret1!=int_ret2)  {
    int_ret = -2;
    error_message("boost_crc32c_u8 <> intel_crc32c_u8. ");
    }
  if(int_ret2!=int_ret3)   {
    int_ret = -3;
    error_message("intel_crc32c_u8 <> intel_crc32c_u64");
    }
  }

  if(bool_chars_need_delete && NULL!=chars)  {
     delete chars;
     chars = NULL;
     }
  if(0==int_ret)    log_message("crc32c test run ok. no error find. ");
  return  int_ret;
}
#endif // DEBUG

#endif // _crc32c_hpp
